package org.babyfish.jimmer.sql.kt.model.logic.instant

import org.babyfish.jimmer.meta.ImmutableType
import org.babyfish.jimmer.meta.LogicalDeletedInfo
import kotlin.test.Test
import kotlin.test.expect
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import java.time.Instant
import kotlin.math.abs

/**
 * Comprehensive test for Instant type support in @LogicalDeleted annotation with KSP processing
 */
class InstantLogicalDeletedKspTest {

    @Test
    fun testInstantNowSupport() {
        val type = ImmutableType.get(EKt::class.java)
        val info = type.logicalDeletedInfo
        assertNotNull(info, "LogicalDeletedInfo should not be null for entity EKt")
        
        // Test that generateValue() returns an Instant close to current time
        val generatedValue = info.generateValue()
        assertTrue(generatedValue is Instant, "Generated value should be an Instant")
        
        val instant = generatedValue as Instant
        val now = Instant.now()
        
        // Verify the generated value is within 1 second of current time
        val timeDiff = abs(instant.toEpochMilli() - now.toEpochMilli())
        assertTrue(timeDiff < 1000, 
                "Generated Instant should be within 1 second of current time. Diff: ${timeDiff}ms")
    }

    @Test
    fun testInstantNullSupport() {
        val type = ImmutableType.get(FKt::class.java)
        val info = type.logicalDeletedInfo
        assertNotNull(info, "LogicalDeletedInfo should not be null for entity FKt")
        
        // Test that generateValue() returns null for @LogicalDeleted("null")
        val generatedValue = info.generateValue()
        expect(null) { generatedValue }
    }

    @Test
    fun testEntityDraftWithInstant() {
        // Test creating entity draft with Instant logical deletion field
        val entity = EKt {
            id = 1L
            deletedTime = null // Explicitly set to null (not deleted)
        }

        expect(1L) { entity.id }
        expect(null) { entity.deletedTime }
    }

    @Test
    fun testEntityDraftWithInstantSet() {
        val testTime = Instant.now()

        // Test creating entity draft with explicitly set Instant
        val entity = EKt {
            id = 2L
            deletedTime = testTime
        }

        expect(2L) { entity.id }
        expect(testTime) { entity.deletedTime }
    }

    @Test
    fun testLogicalDeletedAction() {
        val type = ImmutableType.get(EKt::class.java)
        val info = type.logicalDeletedInfo
        assertNotNull(info, "LogicalDeletedInfo should not be null for entity EKt")
        
        // Test that isDeleted works correctly with Instant values
        val deletedTime = Instant.now()
        
        // Non-null Instant should be considered deleted (for nullable fields with "now" value)
        val isDeleted = info.isDeleted(deletedTime)
        expect(true) { isDeleted } // Non-null means deleted for nullable field

        // Null should indicate not deleted
        val isNotDeleted = info.isDeleted(null)
        expect(false) { isNotDeleted } // Null means not deleted for nullable field
    }

    @Test
    fun testInstantTypeValidation() {
        // Verify that the LogicalDeletedInfo recognizes Instant as a valid time type
        val type = ImmutableType.get(EKt::class.java)
        val info = type.logicalDeletedInfo
        assertNotNull(info, "LogicalDeletedInfo should not be null for entity EKt")
        
        // The fact that we can get LogicalDeletedInfo without exception means
        // Instant is now properly supported in NOW_SUPPLIER_MAP
        expect(Instant::class.java) { info.prop.returnClass }
    }

    @Test
    fun testKspCodeGeneration() {
        // Test that KSP generates the correct Draft classes and metadata
        val type = ImmutableType.get(EKt::class.java)
        assertNotNull(type, "ImmutableType should be generated by KSP")
        
        val prop = type.getProp("deletedTime")
        assertNotNull(prop, "deletedTime property should be found")
        expect(Instant::class.java) { prop.returnClass }
        expect(true) { prop.isNullable }
        
        val info = type.logicalDeletedInfo
        assertNotNull(info, "LogicalDeletedInfo should be generated")
        expect(prop) { info.prop }
    }

    @Test
    fun testKotlinLogicalDeletionBehavior() {
        // Test Kotlin entity logical deletion behavior
        val kotlinType = ImmutableType.get(EKt::class.java)
        val kotlinInfo = kotlinType.logicalDeletedInfo

        assertNotNull(kotlinInfo, "Kotlin entity should have LogicalDeletedInfo")

        // Should generate Instant values for "now" logical deletion
        val kotlinValue = kotlinInfo.generateValue()
        assertTrue(kotlinValue is Instant, "Kotlin entity should generate Instant")

        // Test deletion state checking
        val now = Instant.now()
        expect(false) { kotlinInfo.isDeleted(null) }  // null means not deleted for "now" type
        expect(true) { kotlinInfo.isDeleted(now) }  // non-null means deleted
    }
}
